-------------------------------------------------------------------------------
--
-- A Linear Congruential Generator (LCG)
--
-------------------------------------------------------------------------------

package LCG (sysLCG) where
import FIFO


--import GetPut
class Put c where
    put    :: c a -> a -> Action

class Get c where
    get    :: c a -> a
    drop   :: c a -> Action

instance Put FIFO where
    put f x  = f.enq x

instance Get FIFO where
    get f    = f.first
    drop f   = f.deq
{-

Linear congruential generators are of the form x(n) = a * x(n-1) + b
mod M.  A good discussion of this type is in the text by Press et al.
These are by far the most common form in use, and have periods in the
range 10^6 to 10^9 when using 32 bit words.  Unfortunately, they have
some unpleasant properties.  When taken in pairs, triplets, or
n-tuples, the random values often fall on only a few planes in
n-space.  Using a shuffle box can break up this uniformity.  For more
information on the problems associated with linear congruential
generators, see Marsaglia 1968, Marsaglia 1993 and Sullivan 1993.

Truncated LCGs attempt the eliminate some weakness by not revealing
all the bits of the LCG at each stage, only some of the bits.

Also see http://world.std.com/~franl/crypto/random-numbers.html
which says:

The general form of a linear congruential generator is:

        SEED = (A * SEED + C) mod M

        X = SEED/M

The user supplies an initial integer value for SEED.  On each call to
the random number generator a new value of SEED is calculated by taking
the current value of SEED, multiplying by A, adding C, and taking the
remainder MOD M of the result.  The new value of SEED is an integer
between 0 and M-1.  This new value of SEED is then converted into a
floating point value between 0 inclusive and 1 exclusive by dividing
SEED by M.  The result X is returned as a floating point random number
in the range [0,1).

First note some obvious properties of sequences generated by this method:

    1.  The modulus M is an upper bound on the number of different values
        SEED can take on.

    2.  If the new value of SEED is the same as one we had before,
        the sequence will begin to repeat itself in a cyclic
        manner.

    3.  All sequences generated by a linear congruential formula will
        eventually enter a cycle which repeats itself endlessly.

A good linear congruential formula will generate a long sequence of
numbers before repeating itself.  The maximum length a sequence can
possibly be before repeating itself is M, the modulus.  This is because
there are only M different possible values SEED can take on.  A linear
congruential formula will generate a sequence of maximum length M if and
only if the following three conditions are met (See Knuth for a proof of
this theorem):

      i)  C is relatively prime to M;
     ii)  B = (A - 1) is a multiple of P, for every prime P dividing M;
    iii)  B = (A - 1) is a multiple of 4, if M is a multiple of 4.

-}

-----------
-- TYPES --
-----------

-- This state mechanism is purely so that we can get an initial seed
-- whenever the system is reset, rather than having a preset seed.
-- If we opt for a preset seed, then this state and the "seedFifo"
-- can go away.
data StateType = Waiting | Running | Done
               deriving (Eq,Bits)


---------
-- LCG --
---------

-- n is the seed type
-- m is the extracted value type
interface RandGen n m =
    seed :: n -> Action
    -- rest is like a FIFO
    ldeq :: Action
    lfirst :: m

instance Get (RandGen n) where
    get l = l.lfirst
    drop l = l.ldeq

-- n is how big the generator is and
-- m is how many bits we get out
-- m must be <= n
mkLCG :: (Add l m n) => UInt n -> UInt n -> Module (RandGen (UInt n) (UInt m))
mkLCG a b =
    module
        x :: Reg (UInt n)
        x <- mkRegU

        state :: Reg (StateType)
        state <- mkReg Waiting

        rules
            when state == Running
              ==> let
                      -- the "mod M" is implicit
                      x' = a * x + b
                  in  action
                        x := x'
                        state := Done

        interface
            seed s = action
                        x := s
                        state := Running
                when state == Waiting
            ldeq = state := Waiting
                when state == Done
            lfirst = truncate x
                when state == Done


------------------
-- SAMPLE USAGE --
------------------

-- This choice of A and B satisfies the three conditions given in the
-- discussion at the top, for M = 2^32.  This, however, does not give
-- a truncated LCG.  (For that, simply change the second parameter of
-- RandGen to a smaller number!  Yay type system!)

-- Note that we had to make the multiplier an argument to mkLCG
-- because if it's implemented in verilog, then it isn't
-- parameterizable (in the size of the inputs and outputs).
-- We could, however, have defined a multiplier module which
-- can be instantiated for different sizes, if we really wanted
-- to preserve some elegance in exchange for not having total
-- control over the internals of the multiplier.

sysLCG :: Module Empty
sysLCG =
    module
        lcg :: RandGen (UInt 32) (UInt 20)
        lcg <- mkLCG 69069 1
        started :: Reg Bool
        started <- mkReg False
        rules
          "init":
            when not started
             ==> action { lcg.seed 12345; started := True }
